package org.dreamwork.tools.network.bridge.client.fx.controllers;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Dimension2D;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.GridPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
import org.dreamwork.concurrent.Looper;
import org.dreamwork.config.KeyValuePair;
import org.dreamwork.tools.network.bridge.client.Keys;
import org.dreamwork.tools.network.bridge.client.fx.GuiContext;
import org.dreamwork.tools.network.bridge.client.fx.SystemNotification;
import org.dreamwork.tools.network.bridge.client.fx.js.JSBridge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import static org.dreamwork.tools.network.bridge.client.fx.GuiContext.getResource;

/**
 * Created by seth.yang on 2020/3/18
 */
public abstract class AbstractWebPage implements Initializable {
    @FXML private ImageView btnClose;
    @FXML private ImageView btnMin;
    @FXML private ImageView btnMax;
    @FXML private GridPane titleBar;
    @FXML protected WebView web;
    @FXML private Label title;

    protected Stage stage;
    private double xOffset = 0;
    private double yOffset = 0;
    private final String url;
    private boolean implicitExit = true;
    private boolean resizable = true;

    private IOnCloseListener onCloseListener;
    protected JSBridge bridge;

    private static final Image min         = new Image ("icons/min.png");
    private static final Image min_hover   = new Image ("icons/min-hover.png");
    private static final Image max         = new Image ("icons/max.png");
    private static final Image max_hover   = new Image ("icons/max-hover.png");
    private static final Image close       = new Image ("icons/close.png");
    private static final Image close_hover = new Image ("icons/close-hover.png");

    private final Logger logger = LoggerFactory.getLogger (AbstractWebPage.class);

    AbstractWebPage (String url) {
        if (GuiContext.jar) {
            URL temp = getClass ().getClassLoader ().getResource (url);
            if (temp == null) {
                throw new RuntimeException (url + " not found");
            }
            this.url = temp.toExternalForm ();
        } else {
            // for debug
            java.io.File file = new java.io.File ("../../src/main/resources/", url);
            URL temp = null;
            try {
                temp = file.toURI ().toURL ();
                this.url = temp.toExternalForm ();
            } catch (IOException ex) {
                logger.warn (ex.getMessage (), ex);
                throw new RuntimeException (ex);
            }
        }

        if (logger.isTraceEnabled ()) {
            logger.trace ("trying to load page from: {}", this.url);
        }
    }

    public void setStage (Stage stage) {
        this.stage = stage;
    }

    public void setImplicitExit (boolean implicitExit) {
        this.implicitExit = implicitExit;
    }

    public void setResizable (boolean resizable) {
        this.resizable = resizable;
    }

    public void setOnCloseListener (IOnCloseListener onCloseListener) {
        this.onCloseListener = onCloseListener;
    }

    public JSBridge getBridge () {
        return bridge;
    }

    private void setMaximized () {
        if (stage != null && resizable) {
            stage.setMaximized (!stage.isMaximized ());
            btnMax.setImage (max);
        }
    }

    @Override
    public void initialize (URL location, ResourceBundle resources) {
        bridge = new JSBridge (web.getEngine (), url, this::onPageLoaded);
        bridge.setJavaScriptActionExecutor (this::performAction);

        titleBar.setOnMouseClicked (e -> {
            if (e.getButton () == MouseButton.PRIMARY && e.getClickCount () >= 2) {
                setMaximized ();
            }
        });
        titleBar.setOnMousePressed (e -> {
            xOffset = e.getSceneX ();
            yOffset = e.getSceneY ();
        });
        titleBar.setOnMouseDragged (e -> {
            if (stage != null) {
                stage.setX (e.getScreenX () - xOffset);
                stage.setY (e.getScreenY () - yOffset);
            }
        });

        btnMin.setOnMouseEntered (e-> btnMin.setImage (min_hover));
        btnMin.setOnMouseExited (e->btnMin.setImage (min));
        btnMin.setOnMouseClicked (e->{
            if (stage != null) {
                stage.setIconified (true);
                btnMin.setImage (min);
            }
        });

        if (resizable) {
            btnMax.setOnMouseClicked (e -> setMaximized ());
            btnMax.setOnMouseEntered (e -> btnMax.setImage (max_hover));
            btnMax.setOnMouseExited (e -> btnMax.setImage (max));
        }

        btnClose.setOnMouseEntered (e->btnClose.setImage (close_hover));
        btnClose.setOnMouseExited (e->btnClose.setImage (close));
        btnClose.setOnMouseClicked (e->{
            if (onCloseListener != null) {
                if (onCloseListener.onClose ()) {
                    close ();
                }
            } else {
                close ();
            }
        });
    }

    private void close () {
        Platform.setImplicitExit (implicitExit);
        stage.close ();
        if (!implicitExit) {
            SystemNotification.notify (getResource ("tray.on-hide"));
        }
    }

    public void setTitle (String key, KeyValuePair<?>... parameters) {
        if (logger.isTraceEnabled ()) {
            logger.trace ("setting page title:: {}", key);
        }
        title.setText (getResource (key, parameters));
    }

    public Dimension2D getSize () {
        return null;
    }

    /**
     * 处理 js 的调用请求
     * @param action js 发起的请求
     * @param params 请求参数，
     * @return 处理结果
     */
    protected abstract Object performAction (String action, Object params);

    /**
     * 当 JSBridge 桥接完成时的事件
     * @param window js window 对象
     */
    protected abstract void onPageLoaded (JSObject window);

    protected void background (Runnable runner) {
        Looper.runInLoop (Keys.UI.KEY_UI_BG_THREAD, runner);
    }

    /**
     * 将执行回归的 javafx 主线程执行
     * @param runner 执行器
     */
    protected void post (Runnable runner) {
        if (Platform.isFxApplicationThread ()) {
            runner.run ();
        } else {
            Platform.runLater (runner);
        }
    }
}